#pragma once
                    
#include "nstring.h"
#include "streams.h"
#include "utils.h"

//
// Specialis karakterek: (mint C-ben): 
//  ujsor: '\n'
//  backslash: '\\'
//  idezojel: '\"'
//

// Ha QUOTEDWORD flag be van kapcsolva, akkor az idezojeleket torli a szovegbol (a " -ket)

// A tokenizer CCharStream -eket dolgoz fel, amik nem igazi streamek, mert lehet oket reset -elni is.
// mar nem igaz --->>>  A streamnek irhato-olvashatonak kell lenni a PushBack() fuggveny miatt <<---

// CTokenizerException -t dobhatnak a Next*() fuggvenyek es a Skip() (de nincs jelolve throw() -al)

// CCharStream::PushBack(int ch) : ch -t nem kell figyelembe vennie, az csak egy 'hint'. Az utolso 
// karaktert teszi vissza (CMemCharStream nem is veszi figyelembe, hogy const char * -okra is mukodhessen)

#if defined(_MSC_VER)
  #ifndef strdup
    #define strdup _strdup
  #endif
#endif



// deklaralt osztalyok:
class CTokenizer;
class CTokenizerException;
class CCharStream;
class CTextFileCharStream;
class CMemCharStream;


class CToken
{
public:
    CChString token;
    long position;                      // TODO: nem mindig update -elem!; pozicio a stream -ben
};


class CTokenizer
{
public:         
    enum FLAGS
    {
        CPP_COMMENT = 1,                // a // stilusu komment ervenyes
        QUOTEDWORD = 2,                 // az idezojeles szavakat egybe veszi
        DONTREMOVEQUOTE = 4,            // pl. ne alakitsa at a "\n" -t '\n' -e

        DEFAULT_FLAGS = CPP_COMMENT | QUOTEDWORD
    };

    CTokenizer();
    ~CTokenizer()                       { Close(); }

    const CToken *GetTokenPtr()         { return &m_Current; }


    bool OpenMem(uint8_t *stream, size_t size, unsigned int flags = DEFAULT_FLAGS);
    bool OpenFile(const char *filename, unsigned int flags = DEFAULT_FLAGS);
    bool Open(CCharStream *input, bool bAutoDelInput, unsigned int flags = DEFAULT_FLAGS);
    void Reset();
    void Close();

    typedef int (*ISSPACEFUNC)(int ch);                 // ez a fv. donti el, hogy a karakter elvalaszto-e

    void SetIsSpaceFunc(ISSPACEFUNC pfnIsSpace)         { m_pfnIsSpace = pfnIsSpace; }


    const CChString *Next(bool bThrowEx);               // ha bThrowEx, akkor NULL helyett kivetelt dob

    const CChString *NextTag(const char *before, const char *after);    // NEM vonatkoznak ra a FLAGS -ek (nincs ertelme); <text>Hello</text>  -> 'Hello'
    const CChString *NextTextTag()      { return NextTag("<text>", "</text>"); }

    const CChString *NextEqTag(const char *before, const char *after);
    const CChString *NextEqTextTag()    { return NextEqTag("<text>", "</text>"); }

    const CChString *NextTagVar(const char *var_name, const char *before, const char *after);
    const CChString *NextTextTagVar(const char *var_name)   { return NextTagVar(var_name, "<text>", "</text>"); }

    void     Skip(const char *skip);                    // ha m_Current != "skip", kivetelt dob   

    bool     NextBool();
    bool     NextEqBool();
    bool     NextBoolVar(const char *var_name);

    unsigned NextRGB();
    unsigned NextEqRGB();
    unsigned NextRGBVar(const char *var_name);

    unsigned NextARGB();
    unsigned NextEqARGB();
    unsigned NextARGBVar(const char *var_name);

    int      NextInt();
    int      NextEqInt();
    int      NextIntVar(const char *var_name);

    unsigned NextUInt();
    unsigned NextEqUInt();
    unsigned NextUIntVar(const char *var_name);

    int64_t  NextInt64();
    int64_t  NextEqInt64();
    int64_t  NextInt64Var(const char *var_name);
    
    uint64_t  NextUInt64();
    uint64_t  NextEqUInt64();
    uint64_t  NextUInt64Var(const char *var_name);

    float  NextFloat();
    float  NextEqFloat();
    float  NextFloatVar(const char *var_name);

    char  *NextString(char *buffer, size_t max_buffer);
    char  *NextEqString(char *buffer, size_t max_buffer);
    char  *NextStringVar(const char *var_name, char *buffer, size_t max_buffer);

    const CChString *NextString();
    const CChString *NextEqString();
    const CChString *NextStringVar(const char *var_name);

private:                                    
    unsigned read_color();                          // ARGB beolvasahoz (return: 0..255)

    static int IsSpace(int ch);

    CTokenizer(const CTokenizer&);                  // masolas tiltasa
    CTokenizer& operator= (const CTokenizer &);

private:
    CToken       m_Current;                         // az aktualis token
    unsigned int m_Flags;
    CCharStream *m_pInputStream;
    bool         m_DelInputOnClose;                 // a Close() torolje az m_pInputStream -et?
    ISSPACEFUNC  m_pfnIsSpace;
};


class CTokenizerException
{     
public:
    CTokenizerException(const char *message)                        { msg = strdup(message); XTRACE(message); }
    ~CTokenizerException()                                          { if (msg) free(msg); }

    const char *what()  { return msg; }

    CTokenizerException(const CTokenizerException &e)               { msg = strdup(e.msg); }
    CTokenizerException &operator= (const CTokenizerException &e)   { if (&e != this) { msg = strdup(e.msg); } return *this; }

private:
    char *msg;
};
